// eee.Sheffield.PZ.Imaging
//
// Copyright ?Ping Zou, 2007
// sg71.cherub@gmail.com

using System;
using System.Collections.Generic;
using System.Text;
using eee.Sheffield.PZ.Math;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;

namespace eee.Sheffield.PZ.Imaging.ImageFilter
{
    public class Median : ImageFilter
    {
        #region Fields
        private int _kernelSize;        // kernel width
        private int _percentile;    // 50 - median
        #endregion

        #region Properties
        public int Radius { get { return _kernelSize; } set { _kernelSize = value; } }
        public int Percentile { get { return _percentile; } set { _percentile = value; } }
        #endregion

        #region Constructor
        public Median():this(11, 50, null)
        {            
        }

        public Median(int radius, int percentile):this(radius, percentile, null)
        {            
        }

        public Median(int radius, int percentile, PZMath_matrix roiMatrix)
        {
            // set _raidus odd;            
            if (System.Math.IEEERemainder(radius, 2.0) == 0)
                _kernelSize = radius + 1 > 3 ? radius + 1 : 3;
            else
                _kernelSize = radius > 3 ? radius : 3;
            _percentile = percentile;
            _regionOfInterestMatrix = roiMatrix;
            _flag = "[Image -> Smooth -> Median]";
        }
        #endregion

        #region Apply filter
        protected override PZMath_matrix ApplyDoubleGraylevelImageFilter(PZMath_matrix srcMatrix, out string logText)
        {
            int width = srcMatrix.ColumnCount;
            int height = srcMatrix.RowCount;

            // prepare kernel
            PZMath_matrix kernel = new PZMath_matrix(_kernelSize, _kernelSize);
            int halfKernelSize = (_kernelSize - 1) / 2;

            PZMath_matrix expandSrcMatrix = srcMatrix.BoundMirrorExpand(_kernelSize, _kernelSize);
            int expandWidth = expandSrcMatrix.ColumnCount;
            int expandHeight = expandSrcMatrix.RowCount;
            PZMath_matrix expandDstMatrix = new PZMath_matrix(expandHeight, expandWidth);
            
            int xStart = halfKernelSize;
            int xEnd = width + halfKernelSize;
            int yStart = halfKernelSize;
            int yEnd = height + halfKernelSize;
            bool record;

            // roi exists
            if (_regionOfInterestMatrix != null)
            {
                PZMath_matrix expandROIMatrix = _regionOfInterestMatrix.BoundMirrorExpand(_kernelSize, _kernelSize);
                
                for (int x = xStart; x < xEnd; x++)
                {
                    for (int y = yStart; y < yEnd; y++)
                    {
                        record = true;
                        // inside kernel, copy elements to "kernel"
                        for (int kx = -1 * halfKernelSize; kx <= halfKernelSize; kx++)
                        {
                            for (int ky = -1 * halfKernelSize; ky <= halfKernelSize; ky++)
                            {
                                if (expandROIMatrix[y + ky, x + kx] == 1.0)
                                    // ROI = 1.0 - mask, 0.0 - frame;
                                {
                                    kernel[ky + halfKernelSize, kx + halfKernelSize] = expandSrcMatrix[y + ky, x + kx];
                                }
                                else
                                {
                                    record = false;
                                    break;
                                }
                            }
                        }
                        if (record)
                            expandDstMatrix[y, x] = kernel.Percentile(_percentile);
                        else
                            expandDstMatrix[y, x] = expandSrcMatrix[y, x];
                    }
                }
            }
            else
            {
                for (int x = xStart; x < xEnd; x++)
                {
                    for (int y = yStart; y < yEnd; y++)
                    {
                        // inside kernel, copy elements to "kernel"
                        for (int kx = -1 * halfKernelSize; kx <= halfKernelSize; kx++)
                        {
                            for (int ky = -1 * halfKernelSize; ky <= halfKernelSize; ky++)
                            {
                                kernel[ky + halfKernelSize, kx + halfKernelSize] = expandSrcMatrix[y + ky, x + kx];
                            }
                        }
                        expandDstMatrix[y, x] = kernel.Percentile(_percentile);
                    }
                }
            }
            PZMath_matrix dstMatrix = expandDstMatrix.BoundMirrorShrink(_kernelSize, _kernelSize);
            logText = "succed";
            return dstMatrix;
        } // ApplyDoubleGraylevelImageFilter()

        protected override PZMath_matrix[] ApplyDoubleColourImageFilter(PZMath_matrix[] srcMatrixArray, out string logText)
        {
            // TODO: add this function
            throw new Exception("The method or operation is not implemented.");
        } // ApplyDoubleColourImageFilter()
        #endregion
    }
}
